在 Ruby 中,除了前幾章提到的 "Block" 不是物件以外,其他的東西都是物件。
但那物件又是什麼東西呢?
物件(Object) = 狀態(State) + 行為(Behavior)
在生活中,交通工具、動植物、你跟我,只要是看得到、摸得到都可以稱為物件(Object)。
而這些物件都會有狀態跟行為,像是貓有「橘色的毛」、「胖的」、「6歲」等狀態,當然也會有「吃飯」、「走路」、「睡覺」等行為。
不曉得大家有沒有吃過雞蛋糕,而雞蛋糕的模具就是個類別 (Class) 的概念,而做出來的雞蛋糕的概念可以叫做實體(Instance)。
所以按照上面的例子,我們可以透過類別(class)組織和產生許多具有相似屬性(attributes)和方法(methods)的物件。
在 Ruby 要定義一個累別,使用 class:
class MethodName
  # ...
end
在 Ruby 中,慣例來說 Class 會以大駝峰 (CamelCase) 來命名,像是:User、BlogsController ...等。
我們也可以透過 Class.new('argument') 來建立物件。
class Dog
  def eat(food)
    puts "#{food} 好吃!"
  end
end
yui = Dog.new
yui.eat("西莎")   # 印出 西莎 好吃!
lucky = Dog.new
lucky.eat "牛肉"  # 印出 牛肉 好吃!
這邊我用 Dog 類別做了兩個不同的實體,也就是 yui 、 lucky ,這兩個物件因為都是用 Dog 類別做出來的實體,所以都會有 eat 方法。
現在的雞蛋糕有各式各樣的口味,如果在一開始加入巧克力就可以做出巧克力口味的雞蛋糕。
以上面的例子來看,當使用 new 方法作實體的時候,也可以順便傳參數進去,如下:
class Dog
  def initialize(name, gender)
    @name = name
    @gender = gender
  end
  def say_hi
    puts "hi, my name is #{@name}, I'm #{@gender}." 
  end
end
yui = Dog.new("Yui", "female")
yui.say_hi  # 印出 hi, my name is Yui, I'm female.
若要用 new 方法傳參數進來,在類別裡面必須有個名為 initialize 的方法來接收傳進來的參數。在 initialize 方法裡,常見的手法是會把參數傳進來給內部的實體變數(instance variable)。
Day03 的有提到實體變數,那實體變數是什麼呢?
在 Ruby 裡的實體變數是 @ 開頭的變數,活在每個實體裡的變數,而且每個實體之間互不相影響。
以上面的例子來看, @name 跟 @gender 就是實體變數。
在 Rails 專案中,實體變數使用的頻率很高,最常用的地方是 Controller 與 View 之間的溝通,舉例來說:
class BlogsController < ApplicationController
  def index
    @articles = Article.all  # 取得所有的 Article 資料
  end
end
實體方法就是作用在實體上的方法。
這句話有點像是廢話,舉例來說:
class Dog
  def say_hi(name)
    puts "hello, #{name}"
  end
end
yui = Dog.new
yui.say_hi("Yui") # 印出 hello, Yui
這邊的 say_hi 是作用在 yui 的實體,這稱 say_hi 為實體方法。
類別方法指的是可以作用在類別上的方法。
class BlogsController < ApplicationController
  def index
    @articles = Article.all  # 取得所有的 Article 資料
  end
end
這邊 all 方法是直接作用在 Article 這個類別上,所以稱為類別方法。
self 建立類別方法如果不想這麼麻煩,我也可以用 self ,如下:
class Dog
  def self.all
    # ...
  end
end
這時我就可以用 Dog.all 來呼叫方法。
如果你也不喜歡這樣的寫法也可以這樣寫:
class Dog
  class << self
    def all
      # ...
    end
  end
end
我用下面的例子來總結一下實體方法與類別方法:
class Hello
  def say_instance(name)
    puts "Hi!  我是實體 (#{name}) 方法"
  end
  def self.say_class(name)
    puts "Hello! 我是類別 (#{name}) 方法"
  end
end
greet = Hello.new
greet.say_instance("instance") # Hi!  我是實體 (instance) 方法
Hello.say_class("class")    # Hello! 我是類別 (class) 方法
類別的方法存取限制常見的主要有三種: public 、 protected 以及 private 。
參考資料: